////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include "BaseEntityHandler.hpp"

#include <SmegCore/Entity/BaseEntity.hpp>
#include <SmegCore/Entity/EntityManager.hpp>

#include "../../tinyxml2/tinyxml2.h"
#include "../../Canvas/SmegEditorCanvas.hpp"

#include "../ObjectHandlerFactory.hpp"
#include "../ObjectHandlers.h"

////////////////////////////////////////////////////////////
/// Constructor
////////////////////////////////////////////////////////////
BaseEntityHandler::BaseEntityHandler() :
    BaseObjectHandler(),
    m_CurrentSpriteFilename( wxEmptyString ),
    m_eCurrentAlign( eAlign_Manual )
{
}

////////////////////////////////////////////////////////////
/// Copy Constructor
////////////////////////////////////////////////////////////
BaseEntityHandler::BaseEntityHandler( const BaseEntityHandler& _Copy ) :
    BaseObjectHandler( _Copy ),
    m_CurrentSpriteFilename( _Copy.m_CurrentSpriteFilename ),
    m_eCurrentAlign( _Copy.m_eCurrentAlign )
{
}

////////////////////////////////////////////////////////////
/// Init the handler and setup the properties
////////////////////////////////////////////////////////////
bool BaseEntityHandler::InitFromGrid( wxPropertyGrid* _pPropertyGrid ) {
    bool creationSucess = false;
    
    m_CurrentSpriteFilename = wxEmptyString;

    if ( !m_pObject ) {
        m_pObject = new smeg::core::BaseEntity( m_pCanvas );
    }

    smeg::core::BaseEntity* baseEntity = static_cast< smeg::core::BaseEntity* >( m_pObject );

    if ( BaseObjectHandler::InitFromGrid( _pPropertyGrid ) ) {
        // SPRITE : Load from a file
        wxFileDialog* fileSpriteDlg = new wxFileDialog(m_pCanvas->GetParent(),
                                        _T("Load an image..."),
                                        wxEmptyString,
                                        wxEmptyString,
                                        "Images Files (*.png;*.bmp;*.jpg)|*.png;*.bmp;*.jpg",
                                        wxFD_OPEN | wxFD_FILE_MUST_EXIST);
        if ( wxID_CANCEL != fileSpriteDlg->ShowModal() && baseEntity->SetSprite( fileSpriteDlg->GetPath() )) {
            m_CurrentSpriteFilename = fileSpriteDlg->GetPath(); 
            creationSucess = true;
        }
        fileSpriteDlg->Destroy();

        if ( creationSucess ) {
            // Velocity
            wxPGProperty* pCurProp = _pPropertyGrid->GetProperty("Velocity");
            sf::Vector2f velocity( 
                pCurProp->GetPropertyByName("x")->GetValue().GetAny().As<float>(),
                pCurProp->GetPropertyByName("y")->GetValue().GetAny().As<float>()
                );
            baseEntity->SetVelocity( velocity );

            // Max Speed
            pCurProp = _pPropertyGrid->GetProperty("Max Speed");
            baseEntity->SetMaxSpeed( pCurProp->GetValue().GetAny().As<float>() );

            // Mass
            pCurProp = _pPropertyGrid->GetProperty("Mass");
            baseEntity->SetMass( pCurProp->GetValue().GetAny().As<float>() );

            // Texture
            // // Repeated
            pCurProp = _pPropertyGrid->GetProperty("Repeated");
            wxAny repeatedValue = pCurProp->GetValue();
            baseEntity->SetRepeated( repeatedValue.As<bool>() );

            // // Smooth
            pCurProp = _pPropertyGrid->GetProperty("Smooth");
            wxAny smoothValue = pCurProp->GetValue();
            baseEntity->SetSmooth( repeatedValue.As<bool>() );

            // Sprite
            // // Color
            pCurProp = _pPropertyGrid->GetProperty("Color");
            wxAny colourValue = pCurProp->GetValue();
            wxColour colourWX = colourValue.As<wxColour>();
            baseEntity->SetColor( sf::Color(colourWX.Red(), colourWX.Green(), colourWX.Blue(), colourWX.Alpha()) );

            // // Origin
            pCurProp = _pPropertyGrid->GetProperty("Origin");
            const sf::Vector2f& origin( baseEntity->GetOrigin() );
            pCurProp->GetPropertyByName("x")->SetValueFromString( wxString::Format("%f", origin.x) );
            pCurProp->GetPropertyByName("y")->SetValueFromString( wxString::Format("%f", origin.y) );

            // // Texture Rect
            pCurProp = _pPropertyGrid->GetProperty("Texture Rect");
            const sf::IntRect& textureRect( baseEntity->GetTextureRect() );
            pCurProp->GetPropertyByName("left")->SetValueFromString( wxString::Format("%d", textureRect.left) );
            pCurProp->GetPropertyByName("top")->SetValueFromString( wxString::Format("%d", textureRect.top) );
            pCurProp->GetPropertyByName("width")->SetValueFromString( wxString::Format("%d", textureRect.width) );
            pCurProp->GetPropertyByName("height")->SetValueFromString( wxString::Format("%d", textureRect.height) );

            // Static
            pCurProp = _pPropertyGrid->GetProperty("Is Static");
            wxAny staticValue = pCurProp->GetValue();
            smeg::core::EntityManager::GetInstance()->SetStatic( baseEntity, staticValue.As<bool>() );
        }
    }

    if ( !creationSucess ) {
        smeg::core::EntityManager::GetInstance()->RemoveEntity( baseEntity );
        m_pObject = NULL;
    }

    return creationSucess;
}
    
////////////////////////////////////////////////////////////
/// Init the handler from an XMLElement
/// BaseObjectHandler::InitFromXML need to be called AFTER the specialized class
////////////////////////////////////////////////////////////
bool BaseEntityHandler::InitFromXML( tinyxml2::XMLElement* _pElement, const wxString& _ProjectPath ) {
    bool successInit = true;

    // Get the infos
    tinyxml2::XMLElement* paramElem = _pElement->FirstChildElement( "Param" );
    tinyxml2::XMLElement* hullElem = _pElement->FirstChildElement( "Object" );

    if ( !m_pObject ) {
        m_pObject = new smeg::core::BaseEntity( m_pCanvas, m_pCanvas->GetViewCenter() );
    }
    smeg::core::BaseEntity* baseEntity = static_cast< smeg::core::BaseEntity* >( m_pObject );
    
    if ( BaseObjectHandler::InitFromXML( _pElement, _ProjectPath ) ) {
        while ( paramElem && successInit ) {
            wxString paramName = paramElem->Attribute( "name" );

            if ( "Velocity" == paramName ) {
                baseEntity->SetVelocity( sf::Vector2f( paramElem->FloatAttribute( "x" ),
                                                        paramElem->FloatAttribute( "y" ) ) );
            } else if ( "Max Speed" == paramName ) {
                baseEntity->SetMaxSpeed( paramElem->FloatAttribute( "value" ) );
            } else if ( "Mass" == paramName ) {
                baseEntity->SetMass( paramElem->FloatAttribute( "value" ) );
            } else if ( "Image" == paramName ) {
                m_CurrentSpriteFilename = wxFileName( _ProjectPath + wxFileName::GetPathSeparator() + paramElem->Attribute( "value" ) );
                m_CurrentSpriteFilename.Normalize();

                if ( !baseEntity->SetSprite( m_CurrentSpriteFilename.GetFullPath() ) ) {
                    successInit = false;
                }
            } else if ( "Repeated" == paramName ) {
                baseEntity->SetRepeated( paramElem->BoolAttribute( "value" ) );
            } else if ( "Smooth" == paramName ) {
                baseEntity->SetSmooth( paramElem->BoolAttribute( "value" ) );
            } else if ( "Color" == paramName ) {
                baseEntity->SetColor( sf::Color( paramElem->UnsignedAttribute( "r" ), 
                                                    paramElem->UnsignedAttribute( "g" ), 
                                                    paramElem->UnsignedAttribute( "b" ), 
                                                    paramElem->UnsignedAttribute( "a" ) ) );
            } else if ( "Origin" == paramName ) {
                baseEntity->SetOrigin( sf::Vector2f( paramElem->FloatAttribute( "x" ),
                                                        paramElem->FloatAttribute( "y" ) ) );
            } else if ( "Texture Rect" == paramName ) {
                baseEntity->SetTextureRect( sf::IntRect( paramElem->IntAttribute( "left" ),
                                                        paramElem->IntAttribute( "top" ),
                                                        paramElem->IntAttribute( "width" ),
                                                        paramElem->IntAttribute( "height" ) ) );
            } else if ( "Alignment" == paramName ) {
                m_eCurrentAlign = (eAlignmentType)paramElem->IntAttribute( "value" );
            } else if ( "Is Static" == paramName ) {
                smeg::core::EntityManager::GetInstance()->SetStatic( baseEntity, paramElem->BoolAttribute( "value" ) );
            }

            paramElem = paramElem->NextSiblingElement( "Param" );
        }
    } else {
        successInit = false;
    }

    if ( !successInit ) {
        m_CurrentSpriteFilename = wxEmptyString;
        smeg::core::EntityManager::GetInstance()->RemoveEntity( baseEntity );
        m_pObject = NULL;
    }

    return successInit;
}
    
////////////////////////////////////////////////////////////
/// Add the created object to the scene
////////////////////////////////////////////////////////////
void BaseEntityHandler::AddObjectToScene() {
    smeg::core::EntityManager::GetInstance()->AddEntity( static_cast< smeg::core::BaseEntity* >( m_pObject ) );
}
    
////////////////////////////////////////////////////////////
/// Remove the created object from the scene
////////////////////////////////////////////////////////////
void BaseEntityHandler::RemoveObjectFromScene( bool _bDeleteObject ) {
    smeg::core::EntityManager::GetInstance()->RemoveEntity( static_cast< smeg::core::BaseEntity* >( m_pObject ), _bDeleteObject );
    if ( _bDeleteObject ) {
        m_pObject = NULL;
    }
}
    
////////////////////////////////////////////////////////////
/// Return the value of a property
////////////////////////////////////////////////////////////
wxString BaseEntityHandler::GetValueAsString( const wxString& _PropertyName ) const {
    smeg::core::BaseEntity* baseEntity = static_cast< smeg::core::BaseEntity* >( m_pObject );

    if ( "Velocity.x" == _PropertyName ) {
        return wxString::Format( "%f", baseEntity->GetVelocity().x );
    } else if ( "Velocity.y" == _PropertyName ) {
        return wxString::Format( "%f", baseEntity->GetVelocity().y );
    } else if ( "Max Speed" == _PropertyName ) {
        return wxString::Format( "%f", baseEntity->GetMaxSpeed() );
    } else if ( "Mass" == _PropertyName ) {
        return wxString::Format( "%f", baseEntity->GetMass() );
    } else if ( "Repeated" == _PropertyName ) {
        return ( baseEntity->IsRepeated() ? "true" : "false" );
    } else if ( "Smooth" == _PropertyName ) {
        return ( baseEntity->IsSmooth() ? "true" : "false" );
    } else if ( "Color" == _PropertyName ) {
        const sf::Color& color( baseEntity->GetColor() );
        return wxString::Format( "(%d,%d,%d,%d)", color.r, color.g, color.b, color.a );
    } else if ( "Origin.x" == _PropertyName ) {
        return wxString::Format( "%f", baseEntity->GetOrigin().x );
    } else if ( "Origin.y" == _PropertyName ) {
        return wxString::Format( "%f", baseEntity->GetOrigin().y );
    } else if ( "Texture Rect.left" == _PropertyName ) {
        return wxString::Format( "%d", baseEntity->GetTextureRect().left );
    } else if ( "Texture Rect.top" == _PropertyName ) {
        return wxString::Format( "%d", baseEntity->GetTextureRect().top );
    } else if ( "Texture Rect.width" == _PropertyName ) {
        return wxString::Format( "%d", baseEntity->GetTextureRect().width );
    } else if ( "Texture Rect.height" == _PropertyName ) {
        return wxString::Format( "%d", baseEntity->GetTextureRect().height );
    } else if ( "Alignment" == _PropertyName ) {
        return wxString::Format( "%d", m_eCurrentAlign );
    } else if ( "Is Static" == _PropertyName ) {
        return ( baseEntity->IsStatic() ? "true" : "false" );
    } else {
        return BaseObjectHandler::GetValueAsString( _PropertyName );
    }
}

////////////////////////////////////////////////////////////
/// Handle the change of a property and apply it to the object
////////////////////////////////////////////////////////////
bool BaseEntityHandler::OnPropertyChanged( wxPropertyGrid* _pPropertyGrid, wxPGProperty* _pChangedProperty ) {
    if ( BaseObjectHandler::OnPropertyChanged( _pPropertyGrid, _pChangedProperty ) ) {
        return true;
    }

    smeg::core::BaseEntity* baseEntity = static_cast< smeg::core::BaseEntity* >( m_pObject );
    
    // Handle changes in values, as needed
    const wxString& propName(_pChangedProperty->GetName());
    if ( "Velocity" == propName || "Velocity" == propName.BeforeFirst( '.' ) ) {
        _pChangedProperty = _pPropertyGrid->GetProperty( "Velocity" );
        sf::Vector2f newVelocity = sf::Vector2f(
            _pChangedProperty->GetPropertyByName("x")->GetValue().GetAny().As<float>(),
            _pChangedProperty->GetPropertyByName("y")->GetValue().GetAny().As<float>()
            );

        baseEntity->SetVelocity( newVelocity );
    } else if ( "Max Speed" == propName ) {
        wxAny maxSpeedValue = _pChangedProperty->GetValue();
        baseEntity->SetMaxSpeed( maxSpeedValue.As<float>() );
    } else if ( "Mass" == propName ) {
        wxAny massValue = _pChangedProperty->GetValue();
        baseEntity->SetMass( massValue.As<float>() );
    } else if ( "Image" == propName ) {
        wxString fileName = _pChangedProperty->GetValue().GetString();
        if ( baseEntity->SetSprite( fileName ) ) {
            m_CurrentSpriteFilename = fileName; 
        } else {
	        wxMessageBox(_T("Fail To load sprite!\n"),
	                        _T("Image loading Error"),
	                        wxOK | wxICON_ERROR,
                            m_pCanvas->GetParent());
            _pChangedProperty->SetValueFromString( wxEmptyString );
            m_CurrentSpriteFilename = wxEmptyString; 
        }
    } else if ( "Repeated" == propName ) {
        wxAny repeatedValue = _pChangedProperty->GetValue();
        baseEntity->SetRepeated( repeatedValue.As<bool>() );
    } else if ( "Smooth" == propName ) {
        wxAny smoothValue = _pChangedProperty->GetValue();
        baseEntity->SetSmooth( smoothValue.As<bool>() );
    } else if ( "Color" == propName ) {
        wxAny colourValue = _pChangedProperty->GetValue();
        wxColour colourWX = colourValue.As<wxColour>();
        baseEntity->SetColor( sf::Color( colourWX.Red(), colourWX.Green(), colourWX.Blue(), colourWX.Alpha() ) );
    } else if ( "Origin" == propName || "Origin" == propName.BeforeFirst( '.' ) ) {
        _pChangedProperty = _pPropertyGrid->GetProperty( "Origin" );
        sf::Vector2f newOrigin = sf::Vector2f(
            _pChangedProperty->GetPropertyByName("x")->GetValue().GetAny().As<float>(),
            _pChangedProperty->GetPropertyByName("y")->GetValue().GetAny().As<float>()
            );

        baseEntity->SetOrigin( newOrigin );
    } else if ( "Texture Rect" == propName || "Texture Rect" == propName.BeforeFirst( '.' ) ) {
        _pChangedProperty = _pPropertyGrid->GetProperty( "Texture Rect" );
        sf::IntRect newTextureRect = sf::IntRect(
            _pChangedProperty->GetPropertyByName("left")->GetValue().GetAny().As<int>(),
            _pChangedProperty->GetPropertyByName("top")->GetValue().GetAny().As<int>(),
            _pChangedProperty->GetPropertyByName("width")->GetValue().GetAny().As<int>(),
            _pChangedProperty->GetPropertyByName("height")->GetValue().GetAny().As<int>()
            );

        baseEntity->SetTextureRect( newTextureRect );
    } else if ( "Alignment" == propName ) {
        wxAny alignValue = _pChangedProperty->GetValue();
        m_eCurrentAlign = (eAlignmentType)alignValue.As<int>();
        const sf::IntRect& textureRect( baseEntity->GetTextureRect() );

        _pPropertyGrid->GetProperty("Origin")->Enable( false );
        switch (m_eCurrentAlign) {
        case eAlign_TopLeft:
            _pPropertyGrid->GetProperty("Origin")->SetValueFromString( wxString::Format( "%d; %d", 0, 0 ) );
            baseEntity->SetOrigin( sf::Vector2f( 0, 0 ) );
            break;
        case eAlign_TopCenter:
            _pPropertyGrid->GetProperty("Origin")->SetValueFromString( wxString::Format( "%d; %d", textureRect.width * 0.5f, 0 ) );
            baseEntity->SetOrigin( sf::Vector2f( textureRect.width * 0.5f, 0 ) );
            break;
        case eAlign_TopRight:
            _pPropertyGrid->GetProperty("Origin")->SetValueFromString( wxString::Format( "%d; %d", textureRect.width, 0 ) );
            baseEntity->SetOrigin( sf::Vector2f( textureRect.width, 0 ) );
            break;

        case eAlign_CenterLeft:
            _pPropertyGrid->GetProperty("Origin")->SetValueFromString( wxString::Format( "%d; %d", 0, textureRect.height * 0.5f ) );
            baseEntity->SetOrigin( sf::Vector2f( 0, textureRect.height * 0.5f ) );
            break;
        case eAlign_Center:
            _pPropertyGrid->GetProperty("Origin")->SetValueFromString( wxString::Format( "%d; %d", textureRect.width * 0.5f, textureRect.height * 0.5f ) );
            baseEntity->SetOrigin( sf::Vector2f( textureRect.width * 0.5f, textureRect.height * 0.5f ) );
            break;
        case eAlign_CenterRight:
            _pPropertyGrid->GetProperty("Origin")->SetValueFromString( wxString::Format( "%d; %d", textureRect.width, textureRect.height * 0.5f ) );
            baseEntity->SetOrigin( sf::Vector2f( textureRect.width, textureRect.height * 0.5f ) );
            break;

        case eAlign_BottomLeft:
            _pPropertyGrid->GetProperty("Origin")->SetValueFromString( wxString::Format( "%d; %d", 0, textureRect.height ) );
            baseEntity->SetOrigin( sf::Vector2f( 0, textureRect.height ) );
            break;
        case eAlign_BottomCenter:
            _pPropertyGrid->GetProperty("Origin")->SetValueFromString( wxString::Format( "%d; %d", textureRect.width * 0.5f, textureRect.height ) );
            baseEntity->SetOrigin( sf::Vector2f( textureRect.width * 0.5f, textureRect.height ) );
            break;
        case eAlign_BottomRight:
            _pPropertyGrid->GetProperty("Origin")->SetValueFromString( wxString::Format( "%d; %d", textureRect.width, textureRect.height ) );
            baseEntity->SetOrigin( sf::Vector2f( textureRect.width, textureRect.height ) );
            break;

        default:
            _pPropertyGrid->GetProperty("Origin")->Enable( true );
        }
    } else if ( "Is Static" == propName ) {
        wxAny staticValue = _pChangedProperty->GetValue();
        smeg::core::EntityManager::GetInstance()->SetStatic( baseEntity, staticValue.As<bool>() );
    } else {
        return false;
    }

    return true;
}
   
////////////////////////////////////////////////////////////
/// Create an XML string with the value of the object
////////////////////////////////////////////////////////////
wxString BaseEntityHandler::InnerXMLString( const wxString& _ProjectPath, const wxString& _Tab ) const {
    smeg::core::BaseEntity* entity = static_cast< smeg::core::BaseEntity* >( m_pObject );
    wxFileName tmpSpriteRelative( m_CurrentSpriteFilename );
    tmpSpriteRelative.MakeRelativeTo( _ProjectPath );

    const sf::Vector2f& velocity( entity->GetVelocity() );
    
    const sf::Color& color( entity->GetColor() );
    const sf::Vector2f& origin( entity->GetOrigin() );
    const sf::IntRect& textureRect( entity->GetTextureRect() );

    return BaseObjectHandler::InnerXMLString( _ProjectPath, _Tab ) +
            _Tab + wxT( "<Param name=\"Velocity\" x=\"" ) + wxString::Format( "%f", velocity.x ) + wxT( "\" " ) +
                                            wxT( "y=\"" ) + wxString::Format( "%f", velocity.y ) + wxT( "\" />\n" ) +
            _Tab + wxT( "<Param name=\"Max Speed\" value=\"" ) + wxString::Format( "%f", entity->GetMaxSpeed() ) + wxT( "\" />\n" ) +
            _Tab + wxT( "<Param name=\"Mass\" value=\"" ) + wxString::Format( "%f", entity->GetMass() ) + wxT( "\" />\n" ) +
            _Tab + wxT( "<Param name=\"Image\" value=\"" ) + tmpSpriteRelative.GetFullPath() + wxT( "\" />\n" ) +
            _Tab + wxT( "<Param name=\"Repeated\" value=\"" ) + (entity->IsRepeated() ? wxT( "true" ) : wxT( "false" ) ) + wxT( "\" />\n" ) + 
            _Tab + wxT( "<Param name=\"Smooth\" value=\"" ) + (entity->IsSmooth() ? wxT( "true" ) : wxT( "false" ) ) + wxT( "\" />\n" ) +
            _Tab + wxT( "<Param name=\"Color\" r=\"" ) + wxString::Format( "%d", color.r ) + wxT( "\" " ) +
                    wxT( "g=\"" ) + wxString::Format( "%d", color.g ) + wxT( "\" " ) +
                    wxT( "b=\"" ) + wxString::Format( "%d", color.b ) + wxT( "\" " ) +
                    wxT( "a=\"" ) + wxString::Format( "%d", color.a ) + wxT( "\" />\n" ) +
            _Tab + wxT( "<Param name=\"Origin\" x=\"" ) + wxString::Format( "%f", origin.x ) + wxT( "\" " ) +
                                            wxT( "y=\"" ) + wxString::Format( "%f", origin.y ) + wxT( "\" />\n" ) +
            _Tab + wxT( "<Param name=\"TextureRect\" left=\"" ) + wxString::Format( "%d", textureRect.left ) + wxT( "\" " ) +
                                            wxT( "top=\"" ) + wxString::Format( "%d", textureRect.top ) + wxT( "\" " ) +
                                            wxT( "width=\"" ) + wxString::Format( "%d", textureRect.width ) + wxT( "\" " ) +
                                            wxT( "height=\"" ) + wxString::Format( "%d", textureRect.height ) + wxT( "\" />\n" ) +
            _Tab + wxT( "<Param name=\"Alignement\" value=\"" ) + wxString::Format( "%d", m_eCurrentAlign ) + wxT( "\" />\n" ) +
            _Tab + wxT( "<Param name=\"Is Static\" value=\"" ) + (entity->IsStatic() ? wxT( "true" ) : wxT( "false" ) ) + wxT( "\" />\n" );
}